package com.github.aloxc.plugin.restplus.component.listener;


import com.github.aloxc.plugin.restplus.common.Consts;
import com.github.aloxc.plugin.restplus.component.VTree;
import com.github.aloxc.plugin.restplus.i18n.I18Bundle;
import com.github.aloxc.plugin.restplus.listener.TreeNodeMenuItemActionListener;
import com.google.gson.*;

import javax.swing.*;
import javax.swing.tree.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.*;
import java.util.List;

import static com.github.aloxc.plugin.restplus.common.Consts.*;
import static com.github.aloxc.plugin.restplus.common.Consts.C_STR;

/**
 * vtree显示控制
 */
public class VTreeShow {
    private VTree responseTree;
    private Font font;
    private Map<String, List<TreePath>> searchMap = new HashMap<>();
    private int searchIndex = 0;
    public VTreeShow(VTree responseTree, Font font){

        this.responseTree = responseTree;
        this.font = font;
    }



    private Map jsonEleTreeMap = null;

    public Map getJsonEleTreeMap() {
        return jsonEleTreeMap;
    }
    public Map<String, List<TreePath>> getSearchMap() {
        return searchMap;
    }

    public int getSearchIndex() {
        return searchIndex;
    }

    public void setSearchIndex(int searchIndex) {
        this.searchIndex = searchIndex;
    }

    public void showInTree(String text) {
        //创建树节点
        JsonParser parser = new JsonParser();
        JsonElement jsonEle = parser.parse(text);

        jsonEleTreeMap = new HashMap(2 << 3);
        jsonEleTreeMap.put(responseTree.hashCode(), jsonEle);
        DefaultMutableTreeNode root = objNode("JSON");
        DefaultTreeModel model = (DefaultTreeModel) responseTree.getModel();
        try {
            createJsonTree(jsonEle, root);
            model.setRoot(root);
            setNodeIcon(responseTree);
            expandTree(responseTree, new TreePath(root));
        } catch (Exception ex) {
            root.removeAllChildren();
            model.setRoot(root);
            showMessageDialog(I18Bundle.getMessage("com.github.aloxc.plugin.restplus.response.tree.jsontree.exception.text"), ex.getMessage());
        }
    }

    private void expandTree(JTree tree, TreePath parent) {
        TreeNode node = (TreeNode) parent.getLastPathComponent();
        if (node.getChildCount() >= 0) {
            for (Enumeration<?> e = node.children(); e.hasMoreElements(); ) {
                TreeNode n = (TreeNode) e.nextElement();
                TreePath path = parent.pathByAddingChild(n);
                expandTree(tree, path);
            }
        }
        tree.expandPath(parent);
    }



    public static DefaultMutableTreeNode nullNode(String key) {
        return treeNode(S_NULL + key + SPLIT + "<null>");
    }

    public static DefaultMutableTreeNode nullNode(int index) {
        return nullNode(fkey(index));
    }


    /**
     * 构造json树结构.
     *
     * @param obj   JsonElement
     * @param pNode DefaultMutableTreeNode
     */
    private void createJsonTree(JsonElement obj, DefaultMutableTreeNode pNode) {
        if (obj.isJsonNull()) {
            pNode.add(nullNode("NULL"));
        } else if (obj.isJsonArray()) {
            createJsonArray(obj.getAsJsonArray(), pNode, "[" + obj.getAsJsonArray().size() + "]");
        } else if (obj.isJsonObject()) {
            JsonObject child = obj.getAsJsonObject();
            // DefaultMutableTreeNode node = objNode(key);
            createJsonObject(child, pNode);
            // pNode.add(node);
        } else if (obj.isJsonPrimitive()) {
            JsonPrimitive pri = obj.getAsJsonPrimitive();
            formatJsonPrimitive("PRI", pri, pNode);
        }
    }


    /**
     * 处理json数组.
     *
     * @param arr
     * @param pNode
     * @param key
     */
    private void createJsonArray(JsonArray arr, DefaultMutableTreeNode pNode, String key) {
        int index = 0;
        DefaultMutableTreeNode child = arrNode(key);
        for (Iterator it = arr.iterator(); it.hasNext(); ) {
            JsonElement el = (JsonElement) it.next();
            if (el.isJsonObject()) {
                JsonObject obj = el.getAsJsonObject();
                DefaultMutableTreeNode node = objNode(index);
                createJsonObject(obj, node);
                child.add(node);
            } else if (el.isJsonArray()) {
                JsonArray lst = el.getAsJsonArray();
                createJsonArray(lst, child, fkey(index));
            } else if (el.isJsonNull()) {
                child.add(nullNode(index));
            } else if (el.isJsonPrimitive()) {
                formatJsonPrimitive(fkey(index), el.getAsJsonPrimitive(), child);
            }
            ++index;
        }
        pNode.add(child);
    }

    /**
     * 处理jsoon对象.
     *
     * @param obj
     * @param pNode
     */
    private void createJsonObject(JsonObject obj, DefaultMutableTreeNode pNode) {
        for (Map.Entry<String, JsonElement> el : obj.entrySet()) {
            String key = el.getKey();
            JsonElement val = el.getValue();
            if (val.isJsonNull()) {
                pNode.add(nullNode(key));
            } else if (val.isJsonArray()) {
                createJsonArray(val.getAsJsonArray(), pNode, key);
            } else if (val.isJsonObject()) {
                JsonObject child = val.getAsJsonObject();
                DefaultMutableTreeNode node = objNode(key);
                createJsonObject(child, node);
                pNode.add(node);
            } else if (val.isJsonPrimitive()) {
                JsonPrimitive pri = val.getAsJsonPrimitive();
                formatJsonPrimitive(key, pri, pNode);
            }
        }

    }

    private void formatJsonPrimitive(String key, JsonPrimitive pri, DefaultMutableTreeNode pNode) {
        if (pri.isJsonNull()) {
            pNode.add(nullNode(key));
        } else if (pri.isNumber()) {
            pNode.add(numNode(key, pri.getAsString()));
        } else if (pri.isBoolean()) {
            pNode.add(boolNode(key, pri.getAsBoolean()));
        } else if (pri.isString()) {
            pNode.add(strNode(key, pri.getAsString()));
        } else if (pri.isJsonArray()) {
            createJsonArray(pri.getAsJsonArray(), pNode, key);
        } else if (pri.isJsonObject()) {
            JsonObject child = pri.getAsJsonObject();
            DefaultMutableTreeNode node = objNode(key);
            createJsonObject(child, node);
            pNode.add(node);
        } else if (pri.isJsonPrimitive()) {
            formatJsonPrimitive(key, pri.getAsJsonPrimitive(), pNode);
        }
    }

    private void setNodeIcon(JTree tree) {
        tree.setCellRenderer(new DefaultTreeCellRenderer() {
            @Override
            public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) {
                super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
                DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
                String tmp = node.toString();
                this.setFont(font);
                if (tmp.startsWith(S_ARR)) {
                    this.setIcon(Consts.Icons.A);
                    this.setText(tmp.substring(2));
                } else if (tmp.startsWith(S_STR)) {
                    this.setIcon(Consts.Icons.V);
                    this.setText(tmp.substring(2));
                } else if (tmp.startsWith(S_OBJ)) {
                    this.setIcon(Consts.Icons.O);
                    this.setText(tmp.substring(2));
                } else if (tmp.startsWith(S_NUM)) {
                    this.setIcon(Consts.Icons.N);
                    this.setText(tmp.substring(2));
                } else if (tmp.startsWith(S_NULL)) {
                    this.setIcon(Consts.Icons.K);
                    this.setText(tmp.substring(2));
                } else if (tmp.startsWith(S_BOOL)) {
                    this.setIcon(Consts.Icons.V);
                    this.setText(tmp.substring(2));
                } else {
                    this.setIcon(Consts.Icons.V);
                    this.setText(tmp.substring(2));
                }
                return this;
            }
        });
    }

    public static DefaultMutableTreeNode numNode(String key, String val) {
        return treeNode(S_NUM + key + SPLIT + val);
    }

    public static DefaultMutableTreeNode numNode(int index, String val) {
        return numNode(fkey(index), val);
    }

    public static DefaultMutableTreeNode boolNode(String key, Boolean val) {
        String sVal = "false";
        if (val) {
            sVal = "true";
        }
        return treeNode(S_BOOL + key + SPLIT + sVal);
    }

    public static DefaultMutableTreeNode boolNode(int index, Boolean val) {
        return boolNode(fkey(index), val);
    }

    public static DefaultMutableTreeNode strNode(String key, String val) {
        return treeNode(S_STR + key + SPLIT + "\"" + val + "\"");
    }

    public static DefaultMutableTreeNode strNode(int index, String val) {
        return strNode(fkey(index), val);
    }

    public static DefaultMutableTreeNode objNode(String key) {
        return treeNode(S_OBJ + key);
    }

    public static DefaultMutableTreeNode objNode(int index) {
        return objNode(fkey(index));
    }

    public static DefaultMutableTreeNode arrNode(String key) {
        return treeNode(S_ARR + key);
    }

    public static DefaultMutableTreeNode arrNode(int index) {
        return arrNode(fkey(index));
    }


    public static DefaultMutableTreeNode treeNode(String str) {
        return new DefaultMutableTreeNode(str);
    }

    public static String fkey(int index) {
        return "[" + index + "]";
    }


    public static int getIndex(String str) {
        int index = -1;
        if (str == null || str.length() == 0) {
            return index;
        }
        index = str.lastIndexOf("[");
        if (index >= 0) {
            try {
                index = Integer.parseInt(str.substring(index + 1, str.length() - 1));
            } catch (Exception ex) {
                index = -1;
            }
        }
        return index;
    }

    public static String getKey(String str) {
        int index = -1;
        if (str == null || str.length() == 0) {
            return str;
        }
        index = str.lastIndexOf("[");
        if (index >= 0) {
            return str.substring(0, index);
        }
        return str;

    }

    public static String[] pstr(String str) {
        //类型,key,value
        String[] arr = new String[3];
        arr[0] = str.substring(0, 1);
        int i = str.indexOf(SPLIT);
        if (C_ARR.equals(arr[0])) {
            arr[1] = str.substring(2);
            arr[2] = ARRAY;
        } else if (C_OBJ.equals(arr[0])) {
            arr[1] = str.substring(2);
            arr[2] = OBJECT;
        } else if (C_STR.equals(arr[0])) {
            arr[1] = str.substring(2, i);
            arr[2] = str.substring(i + 4, str.length() - 1);
        } else {
            arr[1] = str.substring(2, i);
            arr[2] = str.substring(i + 3, str.length());
        }
        return arr;
    }

    private void showMessageDialog(String title, String msg) {

    }

    private class ResponseTreeMouseListener implements MouseListener {

        @Override
        public void mouseClicked(MouseEvent e) {
        }

        @Override
        public void mousePressed(MouseEvent e) {
        }

        @Override
        public void mouseReleased(MouseEvent e) {

            TreePath path = responseTree.getPathForLocation(e.getX(), e.getY());
            if (path == null) {
                return;
            }
            responseTree.setSelectionPath(path);
            DefaultMutableTreeNode selNode = (DefaultMutableTreeNode) responseTree.getLastSelectedPathComponent();
            if (e.isPopupTrigger()) {
                JPopupMenu popMenu = new JPopupMenu();
                JMenuItem copyKey = new JMenuItem(I18Bundle.getMessage("com.github.aloxc.plugin.restplus.response.tree.copy.key.text"));
                JMenuItem copyValue = new JMenuItem(I18Bundle.getMessage("com.github.aloxc.plugin.restplus.response.tree.copy.value.text"));
                JMenuItem copyPath = new JMenuItem(I18Bundle.getMessage("com.github.aloxc.plugin.restplus.response.tree.copy.path.text"));
                JMenuItem copyKeyValue = new JMenuItem(I18Bundle.getMessage("com.github.aloxc.plugin.restplus.response.tree.copy.keyvalue.text"));
                JMenuItem copyNode = new JMenuItem(I18Bundle.getMessage("com.github.aloxc.plugin.restplus.response.tree.copy.node.text"));
                JMenuItem copyPathAllVal = new JMenuItem(I18Bundle.getMessage("com.github.aloxc.plugin.restplus.response.tree.copy.samepathvalue.text"));
                JMenuItem copySingleNodeString = new JMenuItem(I18Bundle.getMessage("com.github.aloxc.plugin.restplus.response.tree.copy.map.text"));
                JMenuItem copyNodeFormat = new JMenuItem(I18Bundle.getMessage("com.github.aloxc.plugin.restplus.response.tree.copy.nodeformatvalue.text"));


                popMenu.add(copyKey);
                popMenu.add(copyValue);
                popMenu.add(copyPath);
                popMenu.add(copyNode);
                popMenu.add(copyKeyValue);
                popMenu.add(copySingleNodeString);
                popMenu.add(copyPathAllVal);
                popMenu.add(copyNodeFormat);
                copyKey.addActionListener(new TreeNodeMenuItemActionListener(jsonEleTreeMap,responseTree, 1, selNode));
                copyValue.addActionListener(new TreeNodeMenuItemActionListener(jsonEleTreeMap,responseTree, 2, selNode));
                copyKeyValue.addActionListener(new TreeNodeMenuItemActionListener(jsonEleTreeMap,responseTree, 3, selNode));
                copyPath.addActionListener(new TreeNodeMenuItemActionListener(jsonEleTreeMap,responseTree, 4, path));
                copyPathAllVal.addActionListener(new TreeNodeMenuItemActionListener(jsonEleTreeMap,responseTree, 5, selNode));
                copyNode.addActionListener(new TreeNodeMenuItemActionListener(jsonEleTreeMap,responseTree, 6, path));
                copyNodeFormat.addActionListener(new TreeNodeMenuItemActionListener(jsonEleTreeMap,responseTree, 7, path));
                copySingleNodeString.addActionListener(new TreeNodeMenuItemActionListener(jsonEleTreeMap,responseTree, 8, selNode));
                popMenu.show(e.getComponent(), e.getX(), e.getY());
            }
        }

        @Override
        public void mouseEntered(MouseEvent e) {
        }

        @Override
        public void mouseExited(MouseEvent e) {
        }
    }


    public void findTreeChildValue(String findText) {
        DefaultMutableTreeNode root = (DefaultMutableTreeNode) responseTree.getModel().getRoot();
        Enumeration e = root.depthFirstEnumeration();
        java.util.List<TreePath> treePathList = searchMap.get(findText);
        if (treePathList == null) {
            treePathList = new ArrayList<>();
            searchIndex = 0;
            while (e.hasMoreElements()) {
                DefaultMutableTreeNode node = (DefaultMutableTreeNode) e.nextElement();
                if (node.isLeaf()) {
                    String str = node.toString();
                    if (str.substring(2).indexOf(findText) >= 0) {
                        responseTree.expandPath(new TreePath(node.getPath()));
                        TreePath tp = expandTreeNode(responseTree, node.getPath(), true);
                        treePathList.add(tp);
                    }
                }
            }
            searchMap.put(findText, treePathList);
        }
        if (!treePathList.isEmpty()) {
            if (searchIndex >= treePathList.size()) {
                searchIndex = 0;
            }
            responseTree.setSelectionPath(treePathList.get(searchIndex));
            responseTree.scrollPathToVisible(treePathList.get(searchIndex));
            searchIndex++;

        }
    }

    private TreePath expandTreeNode(JTree tree, TreeNode[] arr, Boolean expand) {
        TreePath[] tp = new TreePath[arr.length];
        tp[0] = new TreePath(arr[0]);
        int pos = 0;
        for (int i = 1; i < arr.length; i++) {
            tp[i] = tp[i - 1].pathByAddingChild(arr[i]);
        }
        for (int i = 0; i < arr.length; i++) {
            if (expand) {
                tree.expandPath(tp[i]);
            } else {
                tree.collapsePath(tp[i]);
            }
            pos = i;
        }
        return tp[pos];
    }


}
